
#include <dbg.hpp>
#include "audio_transcode.hpp"

#undef av_err2str
#define av_err2str(errnum) av_make_error_string((char*)__builtin_alloca(AV_ERROR_MAX_STRING_SIZE), AV_ERROR_MAX_STRING_SIZE, errnum)

//opus to aac

    // AVPacket* depkt_ = nullptr;
    // AVPacket* enpkt_ = nullptr;
    // AVFrame* framein_ = nullptr;
    // AVFrame* frameout_ = nullptr;
    // AVCodecContext* dectx_ = nullptr;
    // AVCodecContext* enctx_ = nullptr;
    // SwrContext *swrctx_ = nullptr;

aTranscode::~aTranscode() {
    if(dectx_) {
        avcodec_free_context(&dectx_);
    }

    if(enctx_) {
        avcodec_free_context(&enctx_);
    }

    if(swrctx_) {
        swr_free(&swrctx_);
    }

    if(framein_) {
        av_frame_free(&framein_);
    }

    if(frameout_) {
        av_frame_free(&frameout_);
    }

    if(depkt_) {
        av_packet_free(&depkt_);
    }

    if(enpkt_) {
        av_packet_free(&enpkt_);
    }
}

int aTranscode::init(int src_sample_rate, int src_channel, int src_bit_rate, int dst_sample_rate, int dst_channel, int dst_bit_rate) {
    if(opened()) {
        return -(__LINE__);
    }

    //audio decode
    AVCodec* pdCodec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
    if (!pdCodec) {
        dbge("Not found decode codec!");
        return -(__LINE__);
    }

    dectx_ = avcodec_alloc_context3(pdCodec);
    if (!dectx_) {
        dbge("Not allocate decode codec context!");
        return -(__LINE__);
    }

    dectx_->codec_id = AV_CODEC_ID_OPUS;
    dectx_->sample_fmt = AV_SAMPLE_FMT_S16;
    dectx_->sample_rate = src_sample_rate;
    dectx_->bit_rate = src_bit_rate;
    dectx_->channels = src_channel;
    dectx_->channel_layout = av_get_default_channel_layout(src_channel);
    dectx_->codec_type = AVMEDIA_TYPE_AUDIO;

    int ret = avcodec_open2(dectx_, pdCodec, nullptr);
    if(ret < 0) {
        dbge("Failed to open decoder! err=%s", av_err2str(ret));
        return -(__LINE__);
    }

    if(!depkt_) {
        depkt_ = av_packet_alloc();
    }

    if(!framein_) {
        framein_ = av_frame_alloc();
    }

    //audio encode
    AVCodec* peCodec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
    if (!peCodec) {
        dbge("Not found decode codec!");
        return -(__LINE__);
    }

    enctx_ = avcodec_alloc_context3(peCodec);
    if (!enctx_) {
        dbge("Not allocate decode codec context!");
        return -(__LINE__);
    }

    enctx_->codec_id = AV_CODEC_ID_AAC;
    enctx_->sample_fmt = AV_SAMPLE_FMT_FLTP;
    enctx_->sample_rate = dst_sample_rate;
    enctx_->bit_rate = dst_bit_rate;
    enctx_->channels = dst_channel;
    enctx_->channel_layout = av_get_default_channel_layout(dst_channel);
    enctx_->codec_type = AVMEDIA_TYPE_AUDIO;

    int ret = avcodec_open2(enctx_, peCodec, nullptr);
    if(ret < 0) {
        dbge("Failed to open decoder! err=%s", av_err2str(ret));
        return -(__LINE__);
    }

    if(!enpkt_) {
        enpkt_ = av_packet_alloc();
    }

    if(!frameout_) {
        frameout_ = av_frame_alloc();
        frameout_->sample_rate = enctx_->sample_rate;
        frameout_->channel_layout = enctx_->channel_layout;
        frameout_->channels = enctx_->channels;
        frameout_->format = enctx_->sample_fmt;
        size_t size = av_samples_get_buffer_size(NULL, enctx_->channels, enctx_->frame_size, enctx_->sample_fmt, 0);
        uint8_t* frame_buf = (uint8_t *)av_malloc(size);
        ret = avcodec_fill_audio_frame(frameout_, enctx_->channels, enctx_->sample_fmt, (const uint8_t*)frame_buf, size, 0);
        if (ret < 0) {
            dbge("avcodec_fill_audio_frame error ");
            return -(__LINE__);
        }
    }

    //audio resample
    swrctx_ = swr_alloc();
    swrctx_ = swr_alloc_set_opts(
        swrctx_
        ,enctx_->channel_layout
        ,enctx_->sample_fmt
        ,enctx_->sample_rate
        ,dectx_->channel_layout
        ,dectx_->sample_fmt
        ,dectx_->sample_rate
        ,0,0
    );

    if(swr_init(swrctx_) != 0) {
        dbge("failed to open swr error ");
        return -(__LINE__);
    }

}
int aTranscode::opened() {
    return nullptr != enctx_ && nullptr != dectx_;
}
int aTranscode::transcode(uint8_t* data, size_t length, const int64_t& pts, const int64_t& dts, const Output& func) {
    if(!opened()) {
        return -(__LINE__);
    }

    depkt_->data = data;
    depkt_->size = length;
    depkt_->pts = pts;
    depkt_->dts = dts;

    return __transcode(depkt_, func);
}

int aTranscode::encode(AVFrame* frame, const Output& func) {
    int ret = avcodec_send_frame(enctx_, frame);
    if (ret) {  
        if (AVERROR(EAGAIN) != ret) {
            // // dbgd(logger_, "Error sending original frame to encoder!");
            return -(__LINE__);
        }
        // avcodec_receive_packet(imgCodecCtx_, outPacaket_);
        // av_packet_unref(outPacaket_);
        // avcodec_send_frame(imgCodecCtx_, frame);
    }

    while (ret >= 0) {
        ret = avcodec_receive_packet(enctx_, enpkt_);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return 0;
        }else if (ret != 0) {
            // NERROR_FMT_SET(ret, "fail to avcodec_receive_packet video, err=[{}]", av_err2str(ret));
            // // dbgd(logger_, "Error receiving encoded frame from encoder!");
            return ret;
        }
        
        func(enpkt_->data, enpkt_->size, enpkt_->pts);

        av_packet_unref(enpkt_);
    }
}
int aTranscode::__transcode(AVPacket* pkt, const Output& func) {
    int ret = avcodec_send_packet(dectx_, pkt);
    if (ret) {
        if (AVERROR(EAGAIN) != ret) {
            dbge("send frame to decoder error. err=%s", av_err2str(ret));
            return -(__LINE__);
        }
        // else {
        //     avcodec_receive_frame(imgCodecCtx_, framein_);
        //     av_frame_unref(inFrame_);
        //     avcodec_send_packet(imgCodecCtx_, imgPacket_);
        // }
    }

    while(ret >= 0) {
        ret = avcodec_receive_frame(dectx_, framein_);
        if (ret) {
            if (AVERROR(EAGAIN) == ret) {
                return ret;
            }
            dbge("receive frame from decoder error. err=%s", av_err2str(ret));
            return -(__LINE__);
        }

        ret = swr_convert_frame(swrctx_, frameout_, framein_);
        if(ret < 0) {
            dbge("receive frame from swr error. err=%s", av_err2str(ret));
            return -(__LINE__);
        }

        frameout_->pts = framein_->pts;
        frameout_->pkt_pts = framein_->pkt_pts;
        frameout_->pkt_dts = framein_->pkt_dts;

        encode(frameout_, func);
        // // fwrite(inFrame_->data[0], sizeof(uint8_t), inFrame_->width*inFrame_->height, fp);
        // // fwrite(inFrame_->data[1], sizeof(uint8_t), inFrame_->width*inFrame_->height/4, fp);
        // // fwrite(inFrame_->data[2], sizeof(uint8_t), inFrame_->width*inFrame_->height/4, fp);

        av_packet_unref(pkt);
    }

    return 0;
}